import inspect
import os
import shutil
import tempfile
import uuid
import atexit
import time
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Optional, Union, Tuple
from src.utils.logger import log_info, log_error, log_warning

class TempFile:
    """表示一个临时文件的类"""
    
    def __init__(self, path: str, category: str = "default", 
                 auto_delete: bool = True, parent=None):
        self.path = path
        self.category = category
        self.auto_delete = auto_delete
        self.creation_time = time.time()
        self.parent = parent
        self.exists = os.path.exists(path)
        self.size = os.path.getsize(path) if self.exists else 0
        self.id = str(uuid.uuid4())
        
    @property
    def name(self) -> str:
        return os.path.basename(self.path)
        
    @property
    def directory(self) -> str:
        return os.path.dirname(self.path)
        
    @property
    def extension(self) -> str:
        return os.path.splitext(self.path)[1]
    
    @property
    def age(self) -> float:
        """获取文件年龄（秒）"""
        return time.time() - self.creation_time
    
    def refresh_info(self) -> None:
        """刷新文件信息"""
        self.exists = os.path.exists(self.path)
        self.size = os.path.getsize(self.path) if self.exists else 0
    
    def delete(self) -> bool:
        """删除此临时文件"""
        if self.exists:
            try:
                os.remove(self.path)
                self.exists = False
                return True
            except Exception as e:
                return False
        return True

class TempDirectory:
    """表示一个临时目录的类"""
    
    def __init__(self, path: str, category: str = "default", 
                 auto_delete: bool = True, parent=None):
        self.path = path
        self.category = category
        self.auto_delete = auto_delete
        self.creation_time = time.time()
        self.parent = parent
        self.id = str(uuid.uuid4())
        self.files: Dict[str, TempFile] = {}
        
    @property
    def name(self) -> str:
        return os.path.basename(self.path)
    
    def add_file(self, path: str, category: str = None) -> TempFile:
        """向目录添加一个临时文件"""
        if not os.path.isabs(path):
            path = os.path.join(self.path, path)
        
        if category is None:
            category = self.category
            
        temp_file = TempFile(path, category, parent=self)
        self.files[temp_file.id] = temp_file
        return temp_file
    
    def delete(self) -> bool:
        """删除此临时目录及其所有内容"""
        if os.path.exists(self.path):
            try:
                shutil.rmtree(self.path)
                return True
            except Exception as e:
                return False
        return True

class FileManager:
    """
    统一的文件管理器，处理临时文件、日志文件和其他文件资源
    """
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(FileManager, cls).__new__(cls)
            cls._instance._initialized = False
        return cls._instance
        
    def __init__(self, app_name: str = "VideoClassifier", 
                 base_temp_dir: str = None, 
                 logger=None):
        if self._initialized:
            return
            
        self.app_name = app_name
        self.logger = logger
        
        # 临时文件存储
        self.temp_files: Dict[str, TempFile] = {}
        self.temp_dirs: Dict[str, TempDirectory] = {}
        
        # 设置临时目录
        if base_temp_dir:
            self.base_temp_dir = base_temp_dir
        else:
            system_temp = tempfile.gettempdir()
            self.base_temp_dir = os.path.join(system_temp, self.app_name)
            
        # 创建基本临时目录
        os.makedirs(self.base_temp_dir, exist_ok=True)
        
        # 日志目录
        self.log_dir = os.path.join(self.base_temp_dir, "logs")
        os.makedirs(self.log_dir, exist_ok=True)
        
        # 应用退出时清理
        atexit.register(self.cleanup_on_exit)
        
        # 初始化完成标记
        self._initialized = True
        self._log_info(f"文件管理器初始化完成：基础临时目录={self.base_temp_dir}")
    
    def get_instance(cls, *args, **kwargs):
        """获取单例实例"""
        return cls(*args, **kwargs)
    
    def create_temp_file(self, prefix: str = "", suffix: str = "", 
                        category: str = None, 
                        auto_delete: bool = True,
                        in_dir: Union[str, TempDirectory] = None) -> TempFile:
        """
        创建新的临时文件
        
        :param prefix: 文件名前缀
        :param suffix: 文件扩展名(如 .mp4)
        :param category: 文件分类，用于组织和批量处理
        :param auto_delete: 是否自动删除
        :param in_dir: 创建在指定目录中，可以是字符串路径或TempDirectory对象
        :return: TempFile对象
        """
        # 确定父目录
        parent_dir = self.base_temp_dir
        parent = None
        
        if category is None:
            category = self._derive_category()
        
        if in_dir:
            if isinstance(in_dir, TempDirectory):
                parent_dir = in_dir.path
                parent = in_dir
            else:
                parent_dir = in_dir
        
        # 生成文件名和路径
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        random_part = uuid.uuid4().hex[:8]
        
        if not prefix:
            prefix = f"temp_{category}_"
            
        filename = f"{prefix}{timestamp}_{random_part}{suffix}"
        filepath = os.path.join(parent_dir, filename)
        
        # 创建TempFile对象
        temp_file = TempFile(filepath, category, auto_delete, parent=parent)
        
        # 记录到管理器
        self.temp_files[temp_file.id] = temp_file
        
        if parent and isinstance(parent, TempDirectory):
            parent.files[temp_file.id] = temp_file
            
        self._log_info(f"创建临时文件: {filepath} (类别={category})")
        return temp_file
    
    def create_temp_dir(self, prefix: str = "", 
                       category: str = None,
                       auto_delete: bool = True,
                       parent_dir: str = None) -> TempDirectory:
        """
        创建新的临时目录
        
        :param prefix: 目录名前缀
        :param category: 目录分类
        :param auto_delete: 是否自动删除
        :param parent_dir: 父目录路径，如果为None则使用base_temp_dir
        :return: TempDirectory对象
        """
        # 确定父目录
        if not parent_dir:
            parent_dir = self.base_temp_dir
            
        if category is None:
            category = self._derive_category()
            
        # 生成目录名
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        random_part = uuid.uuid4().hex[:8]
        
        if not prefix:
            prefix = f"{category}_"
            
        dirname = f"{prefix}{timestamp}_{random_part}"
        dirpath = os.path.join(parent_dir, dirname)
        
        # 创建目录
        os.makedirs(dirpath, exist_ok=True)
        
        # 创建TempDirectory对象
        temp_dir = TempDirectory(dirpath, category, auto_delete)
        
        # 记录到管理器
        self.temp_dirs[temp_dir.id] = temp_dir
        
        self._log_info(f"创建临时目录: {dirpath} (类别={category})")
        return temp_dir
    
    def register_external_file(self, filepath: str, 
                              category: str = "external",
                              auto_delete: bool = False) -> Optional[TempFile]:
        """
        注册外部创建的临时文件
        
        :param filepath: 文件路径
        :param category: 文件分类
        :param auto_delete: 是否自动删除
        :return: TempFile对象，如果文件不存在则返回None
        """
        if not os.path.exists(filepath):
            self._log_warning(f"尝试注册不存在的文件: {filepath}")
            return None
            
        temp_file = TempFile(filepath, category, auto_delete)
        self.temp_files[temp_file.id] = temp_file
        
        self._log_info(f"注册外部文件: {filepath} (类别={category})")
        return temp_file
    
    def register_external_dir(self, dirpath: str,
                             category: str = "external",
                             auto_delete: bool = False) -> Optional[TempDirectory]:
        """
        注册外部创建的临时目录
        
        :param dirpath: 目录路径
        :param category: 目录分类
        :param auto_delete: 是否自动删除
        :return: TempDirectory对象，如果目录不存在则返回None
        """
        if not os.path.exists(dirpath) or not os.path.isdir(dirpath):
            self._log_warning(f"尝试注册不存在的目录: {dirpath}")
            return None
            
        temp_dir = TempDirectory(dirpath, category, auto_delete)
        self.temp_dirs[temp_dir.id] = temp_dir
        
        # 扫描并注册目录中的文件
        for root, _, files in os.walk(dirpath):
            for filename in files:
                filepath = os.path.join(root, filename)
                file_obj = TempFile(filepath, category, auto_delete, parent=temp_dir)
                temp_dir.files[file_obj.id] = file_obj
        
        self._log_info(f"注册外部目录: {dirpath} (类别={category})")
        return temp_dir
    
    def cleanup_category(self, category: str) -> Tuple[int, int]:
        """
        清理指定类别的所有临时文件和目录
        
        :param category: 要清理的类别
        :return: (deleted_files_count, deleted_dirs_count)
        """
        files_deleted = 0
        dirs_deleted = 0
        
        # 清理匹配类别的文件
        files_to_delete = [fid for fid, f in self.temp_files.items() 
                         if f.category == category and f.auto_delete]
        
        for file_id in files_to_delete:
            if self.temp_files[file_id].delete():
                files_deleted += 1
                del self.temp_files[file_id]
        
        # 清理匹配类别的目录
        dirs_to_delete = [did for did, d in self.temp_dirs.items() 
                        if d.category == category and d.auto_delete]
        
        for dir_id in dirs_to_delete:
            if self.temp_dirs[dir_id].delete():
                dirs_deleted += 1
                del self.temp_dirs[dir_id]
        
        self._log_info(f"清理类别 '{category}': 已删除 {files_deleted} 个文件和 {dirs_deleted} 个目录")
        return (files_deleted, dirs_deleted)
    
    def cleanup_older_than(self, max_age_seconds: int) -> Tuple[int, int]:
        """
        清理超过指定年龄的临时文件和目录
        
        :param max_age_seconds: 最大年龄（秒）
        :return: (deleted_files_count, deleted_dirs_count)
        """
        now = time.time()
        files_deleted = 0
        dirs_deleted = 0
        
        # 清理过期文件
        files_to_delete = [fid for fid, f in self.temp_files.items() 
                         if f.auto_delete and (now - f.creation_time) > max_age_seconds]
        
        for file_id in files_to_delete:
            if self.temp_files[file_id].delete():
                files_deleted += 1
                del self.temp_files[file_id]
        
        # 清理过期目录
        dirs_to_delete = [did for did, d in self.temp_dirs.items() 
                        if d.auto_delete and (now - d.creation_time) > max_age_seconds]
        
        for dir_id in dirs_to_delete:
            if self.temp_dirs[dir_id].delete():
                dirs_deleted += 1
                del self.temp_dirs[dir_id]
                
        self._log_info(f"清理旧文件 (>{max_age_seconds}秒): 已删除 {files_deleted} 个文件和 {dirs_deleted} 个目录")
        return (files_deleted, dirs_deleted)
    
    def cleanup_all(self) -> Tuple[int, int]:
        """
        清理所有标记为auto_delete的临时文件和目录
        
        :return: (deleted_files_count, deleted_dirs_count)
        """
        files_deleted = 0
        dirs_deleted = 0
        
        # 首先删除文件
        files_to_delete = [fid for fid, f in self.temp_files.items() if f.auto_delete]
        for file_id in files_to_delete:
            if self.temp_files[file_id].delete():
                files_deleted += 1
                del self.temp_files[file_id]
        
        # 然后删除目录
        dirs_to_delete = [did for did, d in self.temp_dirs.items() if d.auto_delete]
        for dir_id in dirs_to_delete:
            if self.temp_dirs[dir_id].delete():
                dirs_deleted += 1
                del self.temp_dirs[dir_id]
        
        self._log_info(f"清理所有临时文件: 已删除 {files_deleted} 个文件和 {dirs_deleted} 个目录")
        return (files_deleted, dirs_deleted)
    
    def cleanup_on_exit(self) -> None:
        """应用程序退出时清理所有临时文件"""
        self._log_info("应用退出，清理所有临时文件...")
        files, dirs = self.cleanup_all()
        self._log_info(f"应用退出清理完成: 已删除 {files} 个文件和 {dirs} 个目录")
    
    def create_log_file(self, prefix: str = "app_log_", 
                      suffix: str = ".log") -> TempFile:
        """
        创建新的日志文件
        
        :param prefix: 文件名前缀
        :param suffix: 文件扩展名
        :return: TempFile对象
        """
        # 确保日志目录存在
        os.makedirs(self.log_dir, exist_ok=True)
        
        # 生成日志文件名和路径
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{prefix}{timestamp}{suffix}"
        filepath = os.path.join(self.log_dir, filename)
        
        # 创建日志文件（但不删除）
        log_file = TempFile(filepath, "log", auto_delete=False)
        
        # 记录到管理器
        self.temp_files[log_file.id] = log_file
        
        self._log_info(f"创建日志文件: {filepath}")
        return log_file
    
    def _log_info(self, message: str) -> None:
        """记录信息日志"""
        if self.logger:
            self.logger(message)
        else:
            log_info(message)
    
    def _log_warning(self, message: str) -> None:
        """记录警告日志"""
        if self.logger:
            self.logger(message, level="warning")
        else:
            log_warning(message)
    
    def _log_error(self, message: str) -> None:
        """记录错误日志"""
        if self.logger:
            self.logger(message, level="error")
        else:
            log_error(message)
            
    def _derive_category(self):
        """根据调用栈自动派生category名称"""
        try:
            # 获取调用栈信息
            stack = inspect.stack()
            # 跳过当前函数(0)和直接调用函数(1)，查看更上层调用方
            if len(stack) > 2:
                frame = stack[2]
                # 获取类名（如果在类方法内）
                if 'self' in frame.frame.f_locals:
                    class_name = frame.frame.f_locals['self'].__class__.__name__
                else:
                    class_name = "Global"
                # 获取方法名
                method_name = frame.function
                return f"{class_name}_{method_name}"
            return "default"
        except Exception:
            return "default"
        finally:
            # 确保调用栈引用被清理
            del stack